using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using H2.DataTypes;
using System.Reflection;
using H2.Sections;

namespace H2
{
    public class MapStream
    {
        /// <summary>
        /// The Absolute File Path Of The Halo 2 Map
        /// </summary>
        public string FilePath { get { return __filePath; } }
        private string __filePath;

        #region Cache
        internal int IndexOffset;
        internal int FileTableOffset;
        internal int SecondaryMagic;
        internal int TagInfoCount;
        internal int TagInfoIndexOffset;
        internal int MetaStart;
        internal TagInfo Scnr;
        #endregion

        private Stream mapStream;
        private BinaryReader br;
        private BinaryWriter bw;

        #region Instance Data

        private Sections.Header __header__;
        private Sections.StringIDs __stringIDs__;
        private Sections.Index __index__;
        private Sections.Tags __tags__;
        private Sections.Sbsp __sbsp__;
        private Sections.UnicodeCollection __unicode__;
        private Sections.Bitmaps __bitmaps__;

        #endregion

        #region Accessors
        /// <summary>
        /// Gives Access To The Underlying Stream
        /// </summary>
        public Stream BaseStream { get { return mapStream; } }
        /// <summary>
        /// Header
        /// </summary>
        public Sections.Header Header { get { return __header__; } set { __header__ = value; } }
        /// <summary>
        /// StringIDs
        /// </summary>
        public Sections.StringIDs StringIDs { get { return __stringIDs__; } set { __stringIDs__ = value; } }
        /// <summary>
        /// Index
        /// </summary>
        public Sections.Index Index { get { return __index__; } set { __index__ = value; } }
        /// <summary>
        /// Tags
        /// </summary>
        public Sections.Tags Tags { get { return __tags__; } set { __tags__ = value; } }
        /// <summary>
        /// Sbsp
        /// </summary>
        public Sections.Sbsp Sbsp { get { return __sbsp__; } set { __sbsp__ = value; } }
        /// <summary>
        /// Unicode
        /// </summary>
        public Sections.UnicodeCollection Unicode { get { return __unicode__; } set { __unicode__ = value; } }
        /// <summary>
        /// Bitmaps
        /// </summary>
        public Sections.Bitmaps Bitmaps { get { return __bitmaps__; } set { __bitmaps__ = value; } }
        #endregion

        internal MapStream()
        { }

        public MapStream(string filepath, StatusChangeHandler statusChangedHandler)
        {
            StatusChanged += statusChangedHandler;

            __filePath = filepath;
            mapStream = new FileStream(FilePath, FileMode.Open);
            br = new BinaryReader(mapStream);
            bw = new BinaryWriter(mapStream);
            Reload();
        }

        public void Reload()
        {
            Status = "Loading Header...";
            System.Windows.Forms.Application.DoEvents();
            Header = new H2.Sections.Header(this);
            Status = "Loading StringIDs...";
            System.Windows.Forms.Application.DoEvents();
            StringIDs = new H2.Sections.StringIDs(this);
            Status = "Loading Index...";
            System.Windows.Forms.Application.DoEvents();
            Index = new H2.Sections.Index(this);
            Status = "Loading File Table...";
            System.Windows.Forms.Application.DoEvents();
            Tags = new H2.Sections.Tags(this);
            Status = "Loading Sbsps And Lightmaps...";
            System.Windows.Forms.Application.DoEvents();
            Sbsp = new H2.Sections.Sbsp(this);
            Sbsp.CreateCache();
            for (int i = 0; i < Sbsp.SbspInfoCache.Length; i++ )
                Sbsp.SbspInfoCache[i].LightMap.CreatePaletteCache();
            Status = "Loading Unicode Tables...";
            System.Windows.Forms.Application.DoEvents();
            Unicode = new H2.Sections.UnicodeCollection(this);
            Status = "Loading Bitmaps...";
            System.Windows.Forms.Application.DoEvents();
            Bitmaps = new H2.Sections.Bitmaps(this);
            Status = "Ready.";
        }

        /// <summary>
        /// Status
        /// </summary>
        internal string Status { get { return __status__; } set { if (StatusChanged != null) { __status__ = DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + " - " + value; StatusChanged(__status__); } } }
        private string __status__;
        public delegate void StatusChangeHandler(string Status);
        public event StatusChangeHandler StatusChanged;

        /// <summary>
        /// Commits Changes To Map
        /// </summary>
        public void Flush()
        { Status = "Saving Changes...";  bw.Flush(); }


        /// <summary>
        /// Closes The Stream
        /// </summary>
        public void Close()
        { br.Close(); Status = "Map Closed"; }

        /// <summary>
        /// Opens The Closed File For Further Editing
        /// Faster Than Creating A New Stream
        /// </summary>
        public void ReOpen()
        {
            Status = "Opening Map...";
            mapStream = new FileStream(FilePath, FileMode.Open);
            br = new BinaryReader(mapStream);
            bw = new BinaryWriter(mapStream);
        }

        /// <summary>
        /// Calculates And Writes The New Map Signature.
        /// </summary>
        public void Resign()
        {
            Status = "Resigning...";

            long oldPosition = Position;
            int size = (int)Length - 2048;
            int times = size / 4;
            int result = 0;

            Position = 2048;
            for (int i = 0; i < times; i++)
            {
                result ^= ReadInt32();
            }
            Header.Checksum = result;

            Position = oldPosition;
            Status = "Ready.";
            System.Windows.Forms.MessageBox.Show(Header.InternalName + "'s New Signature Is " + result.ToString(), "Resigned");
        }

        public long Position { get { return mapStream.Position; } set { mapStream.Position = value; } }

        public long Length { get { return mapStream.Length; } }

        public void MoveToTag(string Class, string Path)
        { Position = Tags[Class, Path].Offset; }

        public void MoveToTag(int i, Sections.Tags.SearchType st)
        { Position = Tags[i, st].Offset; }

        #region Add Data
        private static void MoveBlock(MapStream map, Type t, int StartOffset, int InsertOffset, int Count, int magic, bool retaincurrentposition)
        {
            long pos = map.Position;
            map.Position = (long)StartOffset;

                        object Block = t.InvokeMember(null,
        BindingFlags.DeclaredOnly |
        BindingFlags.Public | BindingFlags.NonPublic |
        BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[0]);

            FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance);

            foreach (FieldInfo f in fields)
            {
                Type FieldType = f.FieldType;

                if (FieldType.IsArray)
                {
                    if (FieldType == typeof(Byte[]))
                        map.ReadBytes(((byte[])f.GetValue(Block)).Length);
                    else if (FieldType == typeof(Char[]))
                        map.ReadChars(((char[])f.GetValue(Block)).Length);
                    else
                    {
                        FieldType = FieldType.GetElementType();
                        int len = map.ReadInt32();//Count
                        Array chunks = Array.CreateInstance(FieldType, len);
                        int rawoffset = map.ReadInt32();//Read Offset
                        int startoffset = rawoffset - magic;
                        if (startoffset >= InsertOffset)
                        {
                            map.Position -= 4;
                            map.Write(rawoffset + Count);
                        }

                        long currentoffset = map.Position;
                        for (int i = 0; i < chunks.Length; i++)
                        {
                            MoveBlock(map, FieldType, startoffset, InsertOffset, Count, magic, false);
                            startoffset = (int)map.Position;
                        }

                        map.Position = currentoffset;
                    }
                }
                else
                {
                    if (FieldType == typeof(Byte))
                        map.Position += 1;
                    else if (FieldType == typeof(Single))
                        map.Position += 4;
                    else if (FieldType == typeof(Int16))
                        map.Position += 2;
                    else if (FieldType == typeof(Int32))
                        map.Position += 4;
                    else if (FieldType == typeof(Int64))
                        map.Position += 8;
                    else if (FieldType == typeof(UInt16))
                        map.Position += 2;
                    else if (FieldType == typeof(UInt32))
                        map.Position += 4;
                    else if (FieldType == typeof(UInt64))
                        map.Position += 8;
                    else if (FieldType == typeof(H2.DataTypes.String))
                        map.Position += (int)((H2.DataTypes.String)f.GetValue(Block)).Length;
                    else if (FieldType == typeof(Bitmask))
                        map.Position += ((Bitmask)f.GetValue(Block)).ByteCountLength;
                    else if (FieldType == typeof(Dependancy))
                        map.Position += 8;
                    else if (FieldType == typeof(StringID))
                        map.Position += 4;
                    else if (FieldType == typeof(H2.DataTypes.Enum))
                        map.Position += ((H2.DataTypes.Enum)f.GetValue(Block)).ByteCountLength;
                    else
                        throw new Exception("Unknown Type");
                }
            }

            if (retaincurrentposition) map.Position = pos;
        }

        public void AddChunksQuick(int PointerOffset, int ChunkSize, int CloneIndex, int NumberToAdd)
        {
            int oldCount = ReadReflexiveAt(PointerOffset);
            byte[] oldChunks = ReadBytes(ChunkSize * oldCount);
            int Size = (ChunkSize * NumberToAdd) + oldChunks.Length;

            byte[] chunk = new byte[ChunkSize];
            for (int i = CloneIndex * ChunkSize, i2 = 0; i < (CloneIndex * ChunkSize) + ChunkSize; i++)
            {
                chunk[i2] = oldChunks[i];
                i2++;
            }

            int newOffset = (int)Length;
            mapStream.SetLength(newOffset + Size);

            Position = newOffset;
            Write(oldChunks);
            for (int i = 0; i < NumberToAdd; i++)
                Write(chunk);

            Position = PointerOffset;
            Write((int)(oldCount + NumberToAdd));
            Write((int)(newOffset + SecondaryMagic));

            Header.MetaSize += Size;
            //Header.Filesize += Size;
        }

        public void AddBytesAt(int Offset, int Count, bool RetainCurrentPosition)
        {
            int pos = (int)Position;

            #region Meta
            if (Offset > Header.MetaStart)//If Is In Meta Table
            {
                //Update All Tag Offsets
                for (int i = 0; i < Tags.TagInfoCache.Count; i++)
                {
                    Status = "Moving: " + Tags.PathCache[i];
                    System.Windows.Forms.Application.DoEvents();
                    try
                    {
                        if (Tags.TagInfoCache[i].Offset >= Offset)//If Tag Is After Offset Move It
                        {
                            MoveBlock(this, Tags.TagStructureTypeDic[Tags.ClassCache[i]], Tags.TagInfoCache[i].Offset, Offset, Count, SecondaryMagic, false);
                            Tags.TagInfoCache[i].ChangeOffset(Tags.TagInfoCache[i].Offset + Count, SecondaryMagic);//Update TagInfo
                        }
                        else if (Tags.TagInfoCache[i].Offset < Offset && (Tags.TagInfoCache[i].Offset + Tags.TagInfoCache[i].Size) > Offset)//Else If Offset Is In Tag Update Interior
                        {
                            MoveBlock(this, Tags.TagStructureTypeDic[Tags.ClassCache[i]], Tags.TagInfoCache[i].Offset, Offset, Count, SecondaryMagic, false);
                        }
                        //Else Do Nothing (Data Is Unaffected)
                    }
                    catch (Exception e)
                    {
                        System.Windows.Forms.MessageBox.Show(Tags.ClassCache[i] + " - " + Tags.PathCache[i] + "\n\n" + e.Message);
                    }
                }

                Tags.Flush();
            }
            else
            {
                int TrueSecondaryMagic = Index.SecondaryMagic;
                Index.SecondaryMagicConstant -= Count;

                #region Index
                Status = "Moving: Index";
                System.Windows.Forms.Application.DoEvents();
                int IndexOffset = Header.IndexOffset;
                if (IndexOffset > Offset)
                    Header.IndexOffset += Offset;
                else
                {
                    if (IndexOffset + Header.IndexSize > Offset)
                        throw new Exception("You Can't Add Data To The Index!");
                    goto ShiftData;
                }
                #endregion

                #region Bitmap
                TagInfo[] BitmapTagInfos = Tags.GetTagInfoArrayOfClass("bitm");
                string[] BitmapFileNames = Tags.GetStringArrayOfClass("bitm");
                for (int i = 0; i < BitmapTagInfos.Length; i++)
                {
                    Status = "Moving: Bitmap Raw - " + BitmapFileNames[i];
                    System.Windows.Forms.Application.DoEvents();
                    TagStructures.bitm bitm = (TagStructures.bitm)Sections.Tags.BlockFromStream(this, BitmapTagInfos[i].Offset, typeof(TagStructures.bitm), TrueSecondaryMagic, true);
                    FieldInfo startoffsetfield = typeof(TagStructures.bitm._68_bitmap_data).GetField("__StartOffset__", BindingFlags.Instance | BindingFlags.NonPublic);

                    foreach (TagStructures.bitm._68_bitmap_data bd in bitm._68_Bitmap_Data)
                    {
                        //if (bd.PixelOffset > Offset) bd.PixelOffset += Offset;
                        if (bd._28_LOD1_Offset > Offset) bd._28_LOD1_Offset += Offset;
                        if (bd._32_LOD2_Offset > Offset) bd._32_LOD2_Offset += Offset;
                        if (bd._36_LOD3_Offset > Offset) bd._36_LOD3_Offset += Offset;

                        Position = (long)startoffsetfield.GetValue(bd);
                        //int EofMeta =  Tags.GetSize(bd, false);
                        //Write(typeof(TagStructures.bitm._68_bitmap_data), bd, TrueSecondaryMagic, );
                    }
                }
                #endregion

                #region Crazy
                Status = "Moving: Strange File Strings";
                System.Windows.Forms.Application.DoEvents();
                int SFSOffset = Header.StrangeFileStringsOffset;
                if (SFSOffset > Offset)
                    Header.StrangeFileStringsOffset += Offset;
                else
                {
                    if (SFSOffset + Header.StrangeFileStringsSize > Offset)
                        throw new Exception("You Can't Add Data To The Strange File Strings!");
                    goto ShiftData;
                }
                #endregion

                #region Unicode
                Status = "Moving: Unicode";
                System.Windows.Forms.Application.DoEvents();
                if (Unicode.English.StringIndexOffset > Offset) Unicode.English.StringIndexOffset += Offset;
                if (Unicode.English.StringTableOffset > Offset) Unicode.English.StringTableOffset += Offset;

                if (Unicode.Chinese.StringIndexOffset > Offset) Unicode.Chinese.StringIndexOffset += Offset;
                if (Unicode.Chinese.StringTableOffset > Offset) Unicode.Chinese.StringTableOffset += Offset;

                if (Unicode.Dutch.StringIndexOffset > Offset) Unicode.Dutch.StringIndexOffset += Offset;
                if (Unicode.Dutch.StringTableOffset > Offset) Unicode.Dutch.StringTableOffset += Offset;

                if (Unicode.French.StringIndexOffset > Offset) Unicode.French.StringIndexOffset += Offset;
                if (Unicode.French.StringTableOffset > Offset) Unicode.French.StringTableOffset += Offset;

                if (Unicode.Italian.StringIndexOffset > Offset) Unicode.Italian.StringIndexOffset += Offset;
                if (Unicode.Italian.StringTableOffset > Offset) Unicode.Italian.StringTableOffset += Offset;

                if (Unicode.Japanese.StringIndexOffset > Offset) Unicode.Japanese.StringIndexOffset += Offset;
                if (Unicode.Japanese.StringTableOffset > Offset) Unicode.Japanese.StringTableOffset += Offset;

                if (Unicode.Korean.StringIndexOffset > Offset) Unicode.Korean.StringIndexOffset += Offset;
                if (Unicode.Korean.StringTableOffset > Offset) Unicode.Korean.StringTableOffset += Offset;

                if (Unicode.Portuguese.StringIndexOffset > Offset) Unicode.Portuguese.StringIndexOffset += Offset;
                if (Unicode.Portuguese.StringTableOffset > Offset) Unicode.Portuguese.StringTableOffset += Offset;

                if (Unicode.Spanish.StringIndexOffset > Offset) Unicode.Spanish.StringIndexOffset += Offset;
                if (Unicode.Spanish.StringTableOffset > Offset) Unicode.Spanish.StringTableOffset += Offset;
                #endregion

                #region FileIndex
                Status = "Moving: File Index";
                System.Windows.Forms.Application.DoEvents();
                if (Header.FileTableIndexOffset > Offset)
                    Header.FileTableIndexOffset += Offset;
                else
                    goto ShiftData;
                #endregion

                #region FileTable
                Status = "Moving: File Table";
                System.Windows.Forms.Application.DoEvents();
                if (Header.FileTableOffset > Offset)
                    Header.FileTableOffset += Offset;
                else
                    goto ShiftData;
                #endregion

                #region StringIDTable
                Status = "Moving: StringID Table";
                System.Windows.Forms.Application.DoEvents();
                if (Header.StringIDsOffset > Offset)
                    Header.StringIDsOffset += Offset;
                else
                    goto ShiftData;
                #endregion

                #region StringIDIndex
                Status = "Moving: StringID Index";
                System.Windows.Forms.Application.DoEvents();
                if (Header.StringIDsIndex > Offset)
                    Header.StringIDsIndex += Offset;
                else
                    goto ShiftData;
                #endregion

                #region StringID128
                Status = "Moving: StringID128 Table";
                System.Windows.Forms.Application.DoEvents();
                if (Header.StringIDs128Offset > Offset)
                    Header.StringIDs128Offset += Offset;
                else
                    goto ShiftData;
                #endregion

                /////////////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////////////
                throw new Exception("Adding Data Before Lightmap Meta Currently Not Supported");

                //#region Lightmap
                //Status = "Moving: Lightmap";
                ////Update All Tag Offsets
                //for (int i = 0; i < Sbsp.SbspInfoCache.Length; i++)
                //{
                //    if (Sbsp.SbspInfoCache[i].LightMap.Offset > Offset) Sbsp.SbspInfoCache[i].LightMap.Offset += Offset;
                //    else
                //        MoveBlock(this, typeof(TagStructures.ltmp), Sbsp.SbspInfoCache[i].LightMap.Offset, Offset, Count, Sbsp.SbspInfoCache[i].Magic, false);
                //    //Is Magic Right
                //}
                //#endregion

                //#region SBSP
                ////FIX POINTERS
                //Status = "Moving: SBSP";
                //int[] SbspOffsets = new int[Sbsp.BSPCount];
                //for (int i = 0; i < SbspOffsets.Length; i++)
                //    SbspOffsets[i] = Sbsp.SbspInfoCache[i].Offset;

                //int[] SortedOffsets = (int[])SbspOffsets.Clone();
                //Array.Sort(SortedOffsets);
                //if (Offset < SortedOffsets[0])
                //    throw new Exception("Adding Data Before SBSP Meta Currently Not Supported");

                ////Update All Tag Offsets
                //for (int i = 0; i < Sbsp.SbspInfoCache.Length; i++)
                //{
                //    if (Sbsp.SbspInfoCache[i].Offset > Offset) Sbsp.SbspInfoCache[i].Offset += Offset;
                //    else
                //        MoveBlock(this, typeof(TagStructures.sbsp), Sbsp.SbspInfoCache[i].LightMap.Offset, Offset, Count, Sbsp.SbspInfoCache[i].Magic, false);
                //    //Is Magic Right
                //}
                //#endregion
            }
            #endregion

        ShiftData: Status = "Adding Bytes To Map...";
            mapStream.SetLength(mapStream.Length + Count);
            int length = (int)Length;
            for (int i = 0; i < Count; i++)
                WriteAt(length - i, ReadByteAt(length - Count - i, false), false);
            Position = Offset;
            for (int i = 0; i < Count; i++)
                Write('0');

            if (RetainCurrentPosition) Position = pos;
            Status = "Ready.";
        }
        #endregion

        public int CalculatePaddingSize(int DataSize, int PaddingChunkSize)
        {
            int Result = 0;
            Math.DivRem(DataSize, PaddingChunkSize, out Result);
            if (Result == 0) return 0;
            return PaddingChunkSize - Result;
        }

        #region Read
        public byte ReadByte()
        {
            return br.ReadByte();
        }

        public byte ReadByte(bool RetainCurrentPosition)
        {
            byte value = br.ReadByte();
            if (RetainCurrentPosition) Position--;
            return value;
        }

        public byte[] ReadBytes(int Count)
        {
            return br.ReadBytes(Count);
        }

        public byte[] ReadBytes(int Count, bool RetainCurrentPosition)
        {
            byte[] value = br.ReadBytes(Count);
            if (RetainCurrentPosition) Position -= Count;
            return value;
        }

        public char[] ReadChars(int Count)
        {
            char[] value = new char[Count];
            for (int i = 0; i < Count; i++)
            {
                value[i] = Convert.ToChar(br.ReadByte());
            }
            return value;
        }

        public char[] ReadChars(int Count, bool RetainCurrentPosition)
        {
            char[] value = new char[Count];
            for (int i = 0; i < Count; i++) value[i] = Convert.ToChar(br.ReadByte());
            if (RetainCurrentPosition) Position -= Count;
            return value;
        }

        public float ReadFloat()
        {
            return br.ReadSingle();
        }

        public float ReadFloat(bool RetainCurrentPosition)
        {
            float value = br.ReadSingle();
            if (RetainCurrentPosition) Position -= 2;
            return value;
        }

        public short ReadInt16()
        {
            return br.ReadInt16();
        }

        public short ReadInt16(bool RetainCurrentPosition)
        {
            short value = br.ReadInt16();
            if (RetainCurrentPosition) Position -= 2;
            return value;
        }

        public int ReadInt32()
        {
            return br.ReadInt32();
        }

        public int ReadInt32(bool RetainCurrentPosition)
        {
            int value = br.ReadInt32();
            if (RetainCurrentPosition) Position -= 4;
            return value;
        }

        public long ReadInt64()
        {
            return br.ReadInt64();
        }

        public long ReadInt64(bool RetainCurrentPosition)
        {
            long value = br.ReadInt64();
            if (RetainCurrentPosition) Position -= 8;
            return value;
        }

        public ushort ReadUInt16()
        {
            return br.ReadUInt16();
        }

        public ushort ReadUInt16(bool RetainCurrentPosition)
        {
            ushort value = br.ReadUInt16();
            if (RetainCurrentPosition) Position -= 2;
            return value;
        }

        public uint ReadUInt32()
        {
            return br.ReadUInt32();
        }

        public uint ReadUInt32(bool RetainCurrentPosition)
        {
            uint value = br.ReadUInt32();
            if (RetainCurrentPosition) Position -= 4;
            return value;
        }

        public ulong ReadUInt64()
        {
            return br.ReadUInt64();
        }

        public ulong ReadUInt64(bool RetainCurrentPosition)
        {
            ulong value = br.ReadUInt64();
            if (RetainCurrentPosition) Position -= 8;
            return value;
        }

        public Bitmask ReadBitmask(int ByteCount)
        {
            return new Bitmask(br.ReadBytes(ByteCount));
        }

        public Bitmask ReadBitmask(int ByteCount, bool RetainCurrentPosition)
        {
            byte[] BitBytes = br.ReadBytes(ByteCount);

            if (RetainCurrentPosition) Position -= ByteCount;

            return new Bitmask(BitBytes);
        }

        public H2.DataTypes.Enum ReadEnum(int ByteCount)
        {
            if (ByteCount == 1)
                return new H2.DataTypes.Enum(ReadByte());
            else if (ByteCount == 2)
                return new H2.DataTypes.Enum(ReadInt16());
            else if (ByteCount == 4)
                return new H2.DataTypes.Enum(ReadInt32());
            else if (ByteCount == 8)
                return new H2.DataTypes.Enum(ReadInt64());
            return null;
        }

        public H2.DataTypes.Enum ReadEnum(int ByteCount, bool RetainCurrentPosition)
        {
            H2.DataTypes.Enum Enum = null;
            if (ByteCount == 1)
                Enum = new H2.DataTypes.Enum(ReadByte());
            else if (ByteCount == 2)
                Enum = new H2.DataTypes.Enum(ReadInt16());
            else if (ByteCount == 4)
                Enum = new H2.DataTypes.Enum(ReadInt32());
            else if (ByteCount == 8)
                Enum = new H2.DataTypes.Enum(ReadInt64());
            
            if (RetainCurrentPosition) Position -= ByteCount;

            return Enum;
        }

        public Dependancy ReadDependancy()
        {
            Dependancy Dependancy = new Dependancy();
            Dependancy.Class = ReadChars(4);
            Dependancy.Path = Tags.GetTagPath(br.ReadInt32(), Tags.SearchType.ID);
            return Dependancy;
        }

        public Dependancy ReadDependancy(bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Dependancy Dependancy = new Dependancy();
            Dependancy.Class = ReadChars(4);
            Dependancy.Path = Tags.GetTagPath(br.ReadInt32(), Tags.SearchType.ID);
            if (RetainCurrentPosition) Position = oldPosition;
            return Dependancy;
        }

        public StringID ReadStringID()
        {
            StringID sid = StringIDs[ReadUInt16()];
            ReadInt16();//Length
            return sid;
        }

        public StringID ReadStringID(bool RetainCurrentPosition)
        {
            long pos = Position;
            StringID sid = StringIDs[ReadUInt16()];
            ReadInt16();//Length
            Position = pos;
            return sid;
        }

        public H2.DataTypes.String ReadFixedLengthString(H2.DataTypes.Length length, Encoding encoding)
        {
            return new H2.DataTypes.String(length, encoding, ReadString((int)length, false).ToCharArray());
        }

        public H2.DataTypes.String ReadFixedLengthString(H2.DataTypes.Length length, Encoding encoding, bool RetainCurrentPosition)
        {
            return new H2.DataTypes.String(length, encoding, ReadString((int)length, false, RetainCurrentPosition).ToCharArray());
        }

        public H2.DataTypes.String ReadFixedLengthString(H2.DataTypes.Length length)
        {
            return new H2.DataTypes.String(length, Encoding.ASCII, ReadString((int)length, false).ToCharArray());
        }

        public H2.DataTypes.String ReadFixedLengthString(H2.DataTypes.Length length, bool RetainCurrentPosition)
        {
            return new H2.DataTypes.String(length, Encoding.ASCII, ReadString((int)length, false, RetainCurrentPosition).ToCharArray());
        }

        public string ReadString(int CharCount, bool removeNull)
        {
            char[] chars = ReadChars(CharCount);
            return (removeNull ? new string(chars).Split('\0')[0] : new string(chars));
        }

        public string ReadString(int CharCount, bool removeNull, bool RetainCurrentPosition)
        {
            char[] chars = ReadChars(CharCount, RetainCurrentPosition);
            return (removeNull ? new string(chars).Split('\0')[0] : new string(chars));
        }

        public string ReadReverseString(int CharCount, bool removeNull)
        {
            char[] chars = ReadChars(CharCount);
            Array.Reverse(chars);
            return (removeNull ? new string(chars).Split('\0')[0] : new string(chars));
        }

        public string ReadReverseString(int CharCount, bool removeNull, bool RetainCurrentPosition)
        {
            char[] chars = ReadChars(CharCount, RetainCurrentPosition);
            Array.Reverse(chars);
            return (removeNull ? new string(chars).Split('\0')[0] : new string(chars));
        }

        public string ReadUnicodeString(int CharCount, bool removeNull)
        {
            string str;

            Encoding dec = Encoding.Unicode;
            str = dec.GetString(br.ReadBytes(CharCount));
            if(removeNull) return str.Split('\0')[0];
            return str;
        }

        public string ReadUnicodeString(int CharCount, bool removeNull, bool RetainCurrentPosition)
        {
            string str;

            Encoding dec = Encoding.Unicode;
            str = dec.GetString(ReadBytes(CharCount, RetainCurrentPosition));
            if (removeNull) return str.Split('\0')[0];
            return str;
        }

        public string ReadNullTerminatedString()
        {
            string str = "";
            char tempChar;

            for (int x = 0; x < Length; x++)
            {
                tempChar = Convert.ToChar(br.ReadByte());

                if (tempChar == '\0') break;
                str += tempChar;
            }
            return str;
        }

        public string ReadNullTerminatedString(bool RetainCurrentPosition)
        {
            string str = "";
            char tempChar;

            for (int x = 0; x < Length; x++)
            {
                tempChar = Convert.ToChar(br.ReadByte());

                if (tempChar == '\0')
                {
                    if (RetainCurrentPosition) Position -= (x - 1);
                    break;
                }
                str += tempChar;
            }
            return str;
        }

        /// <summary>
        /// If Chunk Count > 0 and Pointer > Meta Table Start, Moves To Pointer And Returns Chunk Count,
        /// If Pointer Is Less Than Meta Table Start Chunk Count Of 0 Is Always Returned
        /// </summary>
        /// <returns>Chunk Count</returns>
        public int ReadReflexive()
        {
            int ChunkCount = br.ReadInt32();

            if (ChunkCount > 0)
            {
                int Pointer = br.ReadInt32() - SecondaryMagic;
                Position = Pointer;
            }
            else br.ReadInt32();//offset

            return ChunkCount;
        }

        public int ReadReflexive(int Magic)
        {
            int ChunkCount = br.ReadInt32();

            if (ChunkCount > 0)
            {
                int Pointer;
                Pointer = br.ReadInt32() - Magic;
                Position = Pointer;
            }
            else br.ReadInt32();//offset

            return ChunkCount;
        }

        public T ReadDataBlock<T>(bool IsSBSP, bool RetainCurrentPosition)
        {
            if (IsSBSP)
                return (T)H2.Sections.Tags.BlockFromStream(this, (int)Position, typeof(T), Index.PrimaryMagic, RetainCurrentPosition);
            return (T)H2.Sections.Tags.BlockFromStream(this, (int)Position, typeof(T), SecondaryMagic, RetainCurrentPosition);
        }
        #endregion

        #region Read At
        public byte ReadByteAt(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            byte value = br.ReadByte();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public byte[] ReadBytesAt(int Offset, int Count, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            byte[] value = br.ReadBytes(Count);
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public char[] ReadCharsAt(int Offset, int Count, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            char[] value = new char[Count];
            for (int i = 0; i < Count; i++) value[i] = Convert.ToChar(br.ReadByte());
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public float ReadFloatAt(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            float value = br.ReadSingle();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public short ReadInt16At(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            short value = br.ReadInt16();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public int ReadInt32At(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            int value = br.ReadInt32();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public long ReadInt64At(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            long value = br.ReadInt64();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public ushort ReadUInt16At(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            ushort value = br.ReadUInt16();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public uint ReadUInt32At(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            uint value = br.ReadUInt32();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public ulong ReadUInt64At(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            ulong value = br.ReadUInt64();
            if (RetainCurrentPosition) Position = oldPosition;
            return value;
        }

        public Bitmask ReadBitmaskAt(int Offset, int ByteCount, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            byte[] BitBytes = br.ReadBytes(ByteCount);

            if (RetainCurrentPosition) Position = oldPosition;

            return new Bitmask(BitBytes);
        }

        public H2.DataTypes.Enum ReadEnumAt(int Offset, int ByteCount, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;

            H2.DataTypes.Enum Enum = null;
            if (ByteCount == 1)
                Enum = new H2.DataTypes.Enum(ReadByte());
            else if (ByteCount == 2)
                Enum = new H2.DataTypes.Enum(ReadInt16());
            else if (ByteCount == 4)
                Enum = new H2.DataTypes.Enum(ReadInt32());
            else if (ByteCount == 8)
                Enum = new H2.DataTypes.Enum(ReadInt64());

            if (RetainCurrentPosition) Position = oldPosition;

            return Enum;
        }

        public Dependancy ReadDependancyAt(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Dependancy Dependancy = new Dependancy();
            Position = Offset;
            Dependancy.Class = ReadChars(4);
            Dependancy.Path = Tags.GetTagPath(br.ReadInt32(), Tags.SearchType.ID);
            if (RetainCurrentPosition) Position = oldPosition;
            return Dependancy;
        }

        public StringID ReadStringIDAt(int Offset, bool RetainCurrentPosition)
        {
            long pos = Position;
            Position = Offset;
            StringID sid = StringIDs[ReadUInt16()];
            ReadInt16();//Length
            if (RetainCurrentPosition) Position = pos;
            return sid;
        }

        public string ReadStringAt(int Offset, int CharCount, bool removeNull, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            char[] chars = ReadChars(CharCount);
            if (RetainCurrentPosition) Position = oldPosition;
            return (removeNull ? new string(chars).Split('\0')[0] : new string(chars));
        }

        public H2.DataTypes.String ReadFixedLengthStringAt(int Offset, H2.DataTypes.Length length, Encoding encoding, bool RetainCurrentPosition)
        {
            return new H2.DataTypes.String(length, encoding, ReadStringAt(Offset, (int)length, false, RetainCurrentPosition).ToCharArray());
        }

        public H2.DataTypes.String ReadFixedLengthStringAt(int Offset, H2.DataTypes.Length length, bool RetainCurrentPosition)
        {
            return new H2.DataTypes.String(length, Encoding.ASCII, ReadStringAt(Offset, (int)length, false, RetainCurrentPosition).ToCharArray());
        }


        public string ReadReverseStringAt(int Offset, int CharCount, bool removeNull, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            char[] chars = ReadChars(CharCount);
            if (RetainCurrentPosition) Position = oldPosition;
            Array.Reverse(chars);
            return (removeNull ? new string(chars).Split('\0')[0] : new string(chars));
        }

        public string ReadUnicodeStringAt(int Offset, int CharCount, bool removeNull, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            string str;

            Encoding dec = Encoding.Unicode;
            str = dec.GetString(br.ReadBytes(CharCount));
            if (RetainCurrentPosition) Position = oldPosition;
            if (removeNull) return str.Split('\0')[0];
            return str;
        }

        public string ReadNullTerminatedStringAt(int Offset, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            StringBuilder str = new StringBuilder();
            char tempChar;

            for (int x = 0; x < Length; x++)
            {
                tempChar = Convert.ToChar(br.ReadByte());
                if (tempChar == '\0')
                {
                    if (RetainCurrentPosition) Position = oldPosition;
                    break;
                }
                str.Append(tempChar);

            }
            return str.ToString();
        }

        /// <summary>
        /// If Chunk Count > 0 and Pointer > Meta Table Start, Moves To Pointer And Returns Chunk Count,
        /// If Pointer Is Less Than Meta Table Start Chunk Count Of 0 Is Always Returned
        /// </summary>
        /// <returns>Chunk Count</returns>
        public int ReadReflexiveAt(int Offset)
        {
            Position = Offset;
            int ChunkCount = br.ReadInt32();

            if (ChunkCount > 0)
            {
                int Pointer = br.ReadInt32() - SecondaryMagic;
                Position = Pointer;
            }

            return ChunkCount;
        }

        public int ReadReflexiveAt(int Offset, int Magic)
        {
            Position = Offset;
            int ChunkCount = br.ReadInt32();

            if (ChunkCount > 0)
            {
                int Pointer = br.ReadInt32() - Magic;
                Position = Pointer;
            }
            else br.ReadInt32();//offset

            return ChunkCount;
        }

        public T ReadDataBlockAt<T>(int Offset, bool IsSBSP, bool RetainCurrentPosition)
        {
            if (IsSBSP)
                return (T)H2.Sections.Tags.BlockFromStream(this, Offset, typeof(T), Index.PrimaryMagic, RetainCurrentPosition);
            return (T)H2.Sections.Tags.BlockFromStream(this, Offset, typeof(T), SecondaryMagic, RetainCurrentPosition);
        }
        #endregion

        #region Write
        public void Write(byte value)
        {
            bw.Write(value);
        }

        public void Write(byte value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position--;
        }

        public void Write(byte[] value)
        {
            bw.Write(value);
        }

        public void Write(byte[] value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= value.Length;
        }

        public void Write(char value)
        {
            bw.Write(Encoding.ASCII.GetBytes(new char[] { value })[0]);
        }

        public void Write(char value, bool RetainCurrentPosition)
        {
            bw.Write(Encoding.ASCII.GetBytes(new char[] { value })[0]);
            if (RetainCurrentPosition) Position--;
        }

        public void Write(char[] value)
        {
            bw.Write(Encoding.ASCII.GetBytes(value));
        }

        public void Write(char[] value, bool RetainCurrentPosition)
        {
            long oldpos = Position;
            bw.Write(Encoding.ASCII.GetBytes(value));
            if (RetainCurrentPosition) Position = oldpos;
        }

        public void Write(float value)
        {
            bw.Write(value);
        }

        public void Write(float value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 4;
        }

        public void Write(short value)
        {
            bw.Write(value);
        }

        public void Write(short value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 2;
        }

        public void Write(int value)
        {
            bw.Write(value);
        }

        public void Write(int value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 4;
        }

        public void Write(long value)
        {
            bw.Write(value);
        }

        public void Write(long value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 8;
        }

        public void Write(ushort value)
        {
            bw.Write(value);
        }

        public void Write(ushort value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 2;
        }

        public void Write(uint value)
        {
            bw.Write(value);
        }

        public void Write(uint value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 4;
        }

        public void Write(ulong value)
        {
            bw.Write(value);
        }

        public void Write(ulong value, bool RetainCurrentPosition)
        {
            bw.Write(value);
            if (RetainCurrentPosition) Position -= 8;
        }

        public void Write(Bitmask Bits)
        {
            bw.Write(Bitmask.ConvertToByteArray(Bits));
        }

        public void Write(Bitmask Bits, bool RetainCurrentPosition)
        {
            bw.Write(Bitmask.ConvertToByteArray(Bits));
            if (RetainCurrentPosition) Position -= Bits.ByteCountLength;
        }

        public void Write(H2.DataTypes.Enum Enum)
        {
            if (Enum.ByteCountLength == 1)
                bw.Write((byte)Enum.Value);
            else if (Enum.ByteCountLength == 2)
                bw.Write((short)Enum.Value);
            else if (Enum.ByteCountLength == 4)
                bw.Write((int)Enum.Value);
            else if (Enum.ByteCountLength == 8)
                bw.Write((long)Enum.Value);
        }

        public void Write(H2.DataTypes.Enum Enum, bool RetainCurrentPosition)
        {
            if (Enum.ByteCountLength == 1)
                bw.Write((byte)Enum.Value);
            else if (Enum.ByteCountLength == 2)
                bw.Write((short)Enum.Value);
            else if (Enum.ByteCountLength == 4)
                bw.Write((int)Enum.Value);
            else if (Enum.ByteCountLength == 8)
                bw.Write((long)Enum.Value);
            if (RetainCurrentPosition) Position -= Enum.ByteCountLength;
        }

        public void Write(Dependancy Dependancy)
        {
            WriteReverseString(Dependancy.ClassString.Replace(Encoding.ASCII.GetChars(new byte[] { 255 })[0], '\0'));
            int i = Tags.IndexOf(Dependancy.ClassString, Dependancy.Path);
            if (i != -1)
                bw.Write(Tags[i, Tags.SearchType.Index].ID);
            else
                bw.Write(i);
        }

        public void Write(Dependancy Dependancy, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            WriteReverseString(Dependancy.ClassString.Replace(Encoding.ASCII.GetChars(new byte[] { 255 })[0], '\0'));
            int i = Tags.IndexOf(Dependancy.ClassString, Dependancy.Path);
            if (i != -1)
                bw.Write(Tags[i, Tags.SearchType.Index].ID);
            else
                bw.Write(i);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void Write(StringID StringID)
        {
            bw.Write(StringIDs.IndexOf(StringID));
            bw.Write(StringID.Length);
        }

        public void Write(StringID StringID, bool RetainCurrentPosition)
        {
            long pos = Position;
            bw.Write(StringIDs.IndexOf(StringID));
            bw.Write(StringID.Length);
            if (RetainCurrentPosition) Position = pos;
        }

        public void Write(Type t, object Object, int Magic, ref int EOFMainMeta)
        {
            FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance);

            foreach (FieldInfo f in fields)
            {
                Type FieldType = f.FieldType;

                if (FieldType.IsArray)
                {
                    if (FieldType == typeof(Byte[]))
                    {
                        byte[] bytes = (byte[])f.GetValue(Object);
                        foreach (byte b in bytes)
                            Write(b);
                    }
                    else if (FieldType == typeof(Char[]))
                    {
                        char[] chars = (char[])f.GetValue(Object);
                        foreach (char c in chars)
                            Write(c);
                    }
                    else
                    {
                        Array chunks = (Array)f.GetValue(Object);
                        Write(chunks.Length);

                        if (chunks.Length > 0)
                        {
                            FieldType = FieldType.GetElementType();

                            Write(EOFMainMeta + Magic);

                            long returnoffset = Position;
                            Position = EOFMainMeta;
                            EOFMainMeta += (H2.TagStructures.GetReflexiveAttributes(chunks).ChunkSize * chunks.Length);
                            FieldInfo startoffsetfield = FieldType.GetField("__StartOffset__", BindingFlags.Instance | BindingFlags.NonPublic);
                            for (int i = 0; i < chunks.Length; i++)
                            {
                                object chunk = chunks.GetValue(i);
                                startoffsetfield.SetValue(chunk, (int)Position);
                                Write(FieldType, chunk, Magic, ref EOFMainMeta);
                            }
                            Position = returnoffset;
                        }
                        else
                            Write((int)0);
                    }
                }
                else if (FieldType == typeof(Byte))
                    Write((byte)f.GetValue(Object));
                else if (FieldType == typeof(Single))
                    Write((float)f.GetValue(Object));
                else if (FieldType == typeof(Int16))
                    Write((short)f.GetValue(Object));
                else if (FieldType == typeof(Int32))
                    Write((int)f.GetValue(Object));
                else if (FieldType == typeof(Int64))
                    Write((long)f.GetValue(Object));
                else if (FieldType == typeof(UInt16))
                    Write((ushort)f.GetValue(Object));
                else if (FieldType == typeof(UInt32))
                    Write((uint)f.GetValue(Object));
                else if (FieldType == typeof(UInt64))
                    Write((ulong)f.GetValue(Object));
                else if (FieldType == typeof(H2.DataTypes.String))
                    Write((H2.DataTypes.String)f.GetValue(Object));
                else if (FieldType == typeof(Bitmask))
                    Write((Bitmask)f.GetValue(Object));
                else if (FieldType == typeof(Dependancy))
                    Write((Dependancy)f.GetValue(Object));
                else if (FieldType == typeof(StringID))
                    Write((StringID)f.GetValue(Object));
                else if (FieldType == typeof(H2.DataTypes.Enum))
                    Write((H2.DataTypes.Enum)f.GetValue(Object));
                else
                    throw new Exception("Unknown Type");
            }
        }

        public void Write(H2.DataTypes.String value)
        {
            Write(value.Encoding.GetBytes(value.ToCharArray()));
        }

        public void Write(H2.DataTypes.String value, bool RetainCurrentPosition)
        {
            Write(value.Encoding.GetBytes(value.ToCharArray()));
            if (RetainCurrentPosition) Position -= (int)value.Length;
        }

        public void Write(string value)
        {
            Write(Encoding.ASCII.GetBytes(value.ToCharArray()));
        }

        public void Write(string value, bool RetainCurrentPosition)
        {
            Write(Encoding.ASCII.GetBytes(value.ToCharArray()));
            if (RetainCurrentPosition) Position -= value.Length;
        }

        public void WriteReverseString(string value)
        {
            char[] Chars = value.ToCharArray();
            Array.Reverse(Chars);
            Write(Chars);
        }

        public void WriteReverseString(string value, bool RetainCurrentPosition)
        {
            char[] Chars = value.ToCharArray();
            Array.Reverse(Chars);
            Write(Chars);
            if (RetainCurrentPosition) Position -= Chars.Length;
        }
        #endregion

        #region Write At
        public void WriteAt(int Offset, byte value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, byte[] value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, char value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, char[] value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            Write(new string(value));
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, float value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, short value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, int value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, long value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, ushort value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, uint value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, ulong value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(value);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, Bitmask Bits, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            bw.Write(Bitmask.ConvertToByteArray(Bits));
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, H2.DataTypes.Enum Enum, bool RetainCurrentPosition)
        {
            long pos = Position;
            Position = Offset;
            if (Enum.ByteCountLength == 1)
                bw.Write((byte)Enum.Value);
            else if (Enum.ByteCountLength == 2)
                bw.Write((short)Enum.Value);
            else if (Enum.ByteCountLength == 4)
                bw.Write((int)Enum.Value);
            else if (Enum.ByteCountLength == 8)
                bw.Write((long)Enum.Value);
            if (RetainCurrentPosition) Position = pos;
        }

        public void WriteAt(int Offset, Dependancy Dependancy, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            WriteReverseString(Dependancy.ClassString.Replace(Encoding.ASCII.GetChars(new byte[] { 255 })[0], '\0'));
            int i = Tags.IndexOf(Dependancy.ClassString, Dependancy.Path);
            if (i != -1)
                bw.Write(Tags[i, Tags.SearchType.Index].ID);
            else
                bw.Write(i);
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, StringID StringID, bool RetainCurrentPosition)
        {
            long pos = Position;
            Position = Offset;
            string str = StringID.ToString();
            bw.Write(StringIDs.IndexOf(str));
            bw.Write((ushort)str.Length);
            if(RetainCurrentPosition) Position = pos;
        }

        public void WriteAt(int Offset, H2.DataTypes.String value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            Write(Encoding.ASCII.GetBytes(value.ToCharArray()));
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteAt(int Offset, string value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            Write(Encoding.ASCII.GetBytes(value.ToCharArray()));
            if (RetainCurrentPosition) Position = oldPosition;
        }

        public void WriteReverseStringAt(int Offset, string value, bool RetainCurrentPosition)
        {
            long oldPosition = Position;
            Position = Offset;
            char[] Chars = value.ToCharArray();
            Array.Reverse(Chars);
            Write(Chars);
            if (RetainCurrentPosition) Position = oldPosition;
        }
        #endregion

    }
}
